/*****************************************************************************/
/**
 *  @file   PixelSend.h
 *  @author Allan Lorant, Naohisa Sakamoto
 */
/*----------------------------------------------------------------------------
 *
 *  Copyright (c) Visualization Laboratory, Kyoto University.
 *  All rights reserved.
 *  See http://www.viz.media.kyoto-u.ac.jp/kvs/copyright/ for details.
 *
 *  $Id$
 */
/*****************************************************************************/
#ifndef PIXEL_SEND_H_INCLUDE
#define PIXEL_SEND_H_INCLUDE

#include <cstring>
#include <iostream>
#include <kvs/PaintEventListener>
#include <kvs/TimeEvent>
#include <flowvr/module.h>
#include "InterfaceBase.h"


namespace PixelSend
{

/*===========================================================================*/
/**
 *  @brief  Interface class for pixel send module.
 */
/*===========================================================================*/
class Interface : public InterfaceBase
{
    typedef InterfaceBase SuperClass;

private:

    int m_size[2];
    unsigned char* m_pixel;

    flowvr::OutputPort* m_size_port;
    flowvr::OutputPort* m_pixel_port;

public:

    Interface( const int width, const int height )
    {
        m_size[0] = width;
        m_size[1] = height;
        m_pixel = new unsigned char [ width * height * 3 ];
        std::memset( m_pixel, 0, width * height * 3 );

        // Declare user defined ports :
        m_pixel_port = new flowvr::OutputPort( "pixelOut" );
        m_size_port = new flowvr::OutputPort( "sizeOut" );

        std::vector<flowvr::Port*> ports;
        ports.push_back( m_pixel_port );
        ports.push_back( m_size_port );

        if ( SuperClass::init( ports ) )
        {
            std::cout << "Can't initialize flowVR module !" << std::endl;
        }
    }

    ~Interface()
    {
        if ( m_pixel ) delete [] m_pixel;
        if ( m_pixel_port ) delete m_pixel_port;
        if ( m_size_port ) delete m_size_port;
    }

    bool read()
    {
        // Blocks until new data is available on the input port.
        if ( !SuperClass::wait() ) { return false; }

        // Get the size of the current display.
        int width = glutGet( GLUT_WINDOW_WIDTH );
        int height = glutGet( GLUT_WINDOW_HEIGHT );
        if ( width != m_size[0] || height != m_size[1] )
        {
            m_size[0] = width;
            m_size[1] = height;

            if ( m_pixel ) delete m_pixel;
            m_pixel = new unsigned char [ width * height * 3 ];
            std::memset( m_pixel, 0, width * height * 3 );
        }

        // Readback of framebuffer
        glReadBuffer( GL_FRONT );
        glPixelStorei( GL_PACK_ALIGNMENT, 1 );
        glReadPixels( 0, 0, m_size[0], m_size[1], GL_RGB, GL_UNSIGNED_BYTE, m_pixel );

        return true;
    }

    bool send()
    {
        // Allocate the messages
        flowvr::MessageWrite size;
        size.data = InterfaceBase::alloc( 2 * sizeof(int) );
        if ( size.data.empty() )
        {
            std::cout << "[Send]Alloc error size" << std::endl;
            return false;
        }

        flowvr::MessageWrite pixel;
        pixel.data = InterfaceBase::alloc( m_size[0] * m_size[1] * 3 );
        if ( pixel.data.empty() )
        {
            std::cout << "[Send]Alloc error framebuffer" << std::endl;
            return false;
        }

        // Copy the data into the messages
        std::memcpy( size.data.writeAccess(), m_size, size.data.getSize() );
        std::memcpy( pixel.data.writeAccess(), m_pixel, pixel.data.getSize() );

        // Send the messages
        InterfaceBase::put( "sizeOut", size );
        InterfaceBase::put( "pixelOut", pixel );
        size.data.clear();
        pixel.data.clear();

        return true;
    }
};

/*===========================================================================*/
/**
 *  @brief  Timer event which send a message each t time.
 */
/*===========================================================================*/
class TimerEvent : public kvs::TimerEventListener
{
    Interface* m_interface;

public:

    TimerEvent( Interface* interface ):
        m_interface( interface ) {}

    void update( kvs::TimeEvent* event )
    {
        m_interface->read();
        m_interface->send();
    }
};

/*===========================================================================*/
/**
 *  @brief  Paint event which send a message each time the display changes.
 */
/*===========================================================================*/
class PaintEvent : public kvs::PaintEventListener
{
    Interface* m_interface;

public:

    PaintEvent( Interface* interface ):
        m_interface( interface ) {}

    void update()
    {
        m_interface->read();
        m_interface->send();
    }
};

} // end of namespace PixelSend

#endif // PIXEL_SEND_H_INCLUDE
